package core.lexical;

import core.Constants;
import core.LexicalToken;
import core.Signal;
import core.Token;
import core.Type;

/**
 * It classifies a token adding a Signal to it and promoving it into a LexicalToken
 * <code>Token</code> according to the FORD language code.
 *
 */
public class LexicalClassificator {

	/**
	 * Classifies the token according to the kind.
	 * @param token - the token to be classified.
	 * @return 
	 */
	public LexicalToken classify(Token token) {
		if(token.getToken().equals(Constants.EOF_S)){
			return new LexicalToken( token, Signal.EOF );
		}
		char firsCharacter = token.getToken().charAt(0);
		if(Character.isLetter(firsCharacter) && Character.isLowerCase(firsCharacter)){
			return recognizeIdentifier(token.getToken());
		}else if(Character.isDigit(firsCharacter)){
			return recognizeNumber(token.getToken());
		}else{
			return recognizeOperator(token.getToken());
		}
		
	}
	
	/**
	 * Recognizes an operator and returns a LexicalToken (the lexical analyzer output).
	 * @param token - the token which content is an operator to be classified.
	 * @return LexicalToken - a lexical token recognized as an operator.
	 */
	private LexicalToken recognizeOperator(String token) {
		if(token.length() == 1){
			char firstChar = token.charAt(0);
	
			switch (firstChar) {
			case '+':
				return new LexicalToken(token, Signal.ADD_OP);
			case '-':
				return new LexicalToken(token, Signal.MINUS_OP);
			case '*':
				return new LexicalToken(token, Signal.TIMES_OP);
			case '/':
				return new LexicalToken(token, Signal.DIVISION_OP);
			case '(':
				return new LexicalToken(token, Signal.OPENING_PARENTHESES);
			case ')':
				return new LexicalToken(token, Signal.CLOSING_PARENTHESES);
			case '{':
				return new LexicalToken(token, Signal.OPENING_BRACE);
			case '}':
				return new LexicalToken(token, Signal.CLOSING_BRACE);
			case '=':
				return new LexicalToken(token, Signal.ATRIBUTION_OPERANT);
			case '>':
				return new LexicalToken(token, Signal.GREATER_THAN_OP);
			case '<':
				return new LexicalToken(token, Signal.LESS_THAN_OP);
			case '!':
				return new LexicalToken(token, Signal.NOT_EQUALS_OP);
			case '&':
				return new LexicalToken(token, Signal.AND_OP);
			case ';':
				return new LexicalToken(token, Signal.END_OF_COMMAND);
			case '[':
				return new LexicalToken(token, Signal.OPENING_BRACKET);
			case ']':
				return new LexicalToken(token, Signal.CLOSING_BRACKET);
			}
		}else if(token.equals("==")){
			return new LexicalToken(token, Signal.EQUAL_OP);
		}else if(token.equals("<=")){
			return new LexicalToken(token, Signal.LESS_OR_EQUAL_THAN_OP);
		}else if(token.equals(">=")){
			return new LexicalToken(token, Signal.GREATER_OR_EQUAL_THAN_OP);
		}else if(token.equals("!=")){
			return new LexicalToken(token, Signal.NOT_EQUALS_OP);
		}else if(token.equals("||")){
			return new LexicalToken(token, Signal.OR_OP);
		}else if(token.startsWith("\"")){
			return new LexicalToken(token, Signal.STRING);
		}else if(token.startsWith("\'")){
			return new LexicalToken(token, Signal.CHAR);
		}

		
		return null;
	}
	
	/**
	 * Recognizes a number and returns a LexicalToken (the lexical analyzer output).
	 * @param token - the token which content is a number to be recognized.
	 * @return LexicalToken - a lexical token recognized as a number.
	 */
	private LexicalToken recognizeNumber(String token) {
		if(Integer.parseInt(token) < Integer.MIN_VALUE){
			return new LexicalToken(Integer.MIN_VALUE + "", Signal.NUMBER);
		}
		
		if(Integer.parseInt(token) > Integer.MAX_VALUE){
			return new LexicalToken(Integer.MAX_VALUE + "", Signal.NUMBER);
		}
		
		return new LexicalToken(token, Signal.NUMBER);
	}

	/**
	 * Recognizes an identifier and returns a LexicalToken (the lexical analyzer output).
	 * @param token - the token which content is an identifier to be recognized.
	 * @return LexicalToken - a lexical token recognized as an identifier.
	 */
	private LexicalToken recognizeIdentifier(String token) {
		
		if(token.length() > Constants.MAX_IDENT){
			return new LexicalToken(token.substring(0, Constants.MAX_IDENT), 
					                Signal.IDENTIFIER);
		}
		
		for (Type type : Type.values()) {
			if( type.hasSameToken(token) ){
				return new LexicalToken(token, Signal.TYPE);
			}
		}
		
		for (String palavra : Constants.KEY_WORDS) {
			if(token.equals(palavra)){
				return new LexicalToken(token, Signal.KEY_WORD);
			}
		}

		return new LexicalToken(token, Signal.IDENTIFIER);
	}

}

